from HelperClass.DataReader_1_0 import *  # 读取数据的包
from matplotlib import pyplot as plt  # 画图库，常用于数据可视化

file_name = "data.npz"  # 样本文件

class NeuralNet_0_1(object):
    def __init__(self, eta):
        """
        初始化神经网络
        :param eta: 学习率
        """
        self.eta = eta  # 学习率
        self.w = 0  # 权重，初始化为0
        self.b = 0  # 偏置，初始化为0

    def __forward(self, x):
        """
        前向計算，輸入x計算预测值z=x*w+b
        :param x:輸入
        :return:计算模型预测值z
        """
        z = x * self.w + self.b  # 简单线性模型z=w*x+b
        return z

    def __backward(self, x, y, z):
        """
        反向計算
        :param x: 輸入值
        :param y: 標簽值、真实值
        :param z: 計算模型预测结果
        :return: w和b的梯度
        """
        dz = z - y  # 误差，用于计算梯度
        db = dz  # 偏置b的梯度
        dw = x * dz  # 权重w的梯度
        return dw, db

    def __update(self, dw, db):
        """
        更新权值和偏置
        :param dw: b的梯度
        :param db: b的梯度
        :return:
        """
        self.w = self.w - self.eta * dw
        self.b = self.b - self.eta * db

    def train(self, dataReader):
        """
        訓練數據，讀取訓練數據，前向計算，反向計算，更新權值
        :param dataReader: （读取）数据的对象
        :return:
        """
        for i in range(dataReader.num_train):
            x, y = dataReader.GetSingleTrainSample(i)  # 读取单样本
            z = self.__forward(x)  # 前向计算函数计算预测值
            dw, db = self.__backward(x, y, z)  # 获取wb的梯度
            self.__update(dw, db)  # 更新权重w和偏置b

    def inference(self, x):
        """
        返回前向計算的模型预测值
        :param x:输入（集合）
        :return:模型预测值（集合）
        """
        return self.__forward(x)

    def ShowResult(self, net, dataReader):
        """
        数据可视化
        :param net: 神经网络对象
        :param dataReader: 数据集对象
        :return:
        """
        X, Y = dataReader.GetWholeTrainSamples()  # 读取所有的数据分别存储在x，y一维数组中
        plt.plot(X, Y, "r.")  # 将x，y中的数据一一画在面板上
        PX = np.linspace(0, 1, 10)  # 均匀间隔创建序列
        PZ = net.inference(PX)  # 获得对应的标签序列
        plt.plot(PX, PZ, "b")  # 画回归结果
        plt.title(f"Air Conditioner Power\neta={net.eta}")  # 面板标题
        plt.xlabel("Number of Servers(k)")  # 面板图像的x标签
        plt.ylabel("Power of Air Conditioner(KW)")  # 面板图像的y标签
        plt.show()  # 展示图象
